local table_insert = table.insert
local table_sort = table.sort
local table_remove = table.remove
local math_floor = math.floor
local math_ceil = math.ceil
local table_unpack = table.unpack
--TODO random函数是非线程安全的，这个方法后期要自己实现线程安全的版本
local math_random = math.random
--math.randomseed(os.time())
local util = require "util"
local zjh_rule = require "zhajinhua.zjh_rule"
local constant = require "constant"

local M = {}

local MAX_PLAYER_NUM = 5
local MIN_PLAYER_NUM = 2
local MAX_WATCH_PLAYER_NUM = 100 --观战列表
local GAME_REST_TIME = 3 --每局有3秒的休息时间
local PLAY_TIME = 15
local COINS_ANIMATION_TIME = 4
local COMPARE_ANIMATION_TIME = 4

local SIGN_CLUB = 1         --梅花
local SIGN_DIAMOND = 2      --钻石
local SIGN_HEART = 3        --桃心
local SIGN_SPADE = 4        --黑桃

--local GAME_OVER_BY_COMPARE = 1

--[[
    每张卡牌分别由1-10,J,Q,K,black joker,red jocker 定义为1-15
--]]

local GAME_STATUS_INIT = 0          --初始化状态
local GAME_STATUS_STARTED = 1       --游戏已经开始
local GAME_STATUS_OVER = 2          --结束状态
local ZHJ_ANG_MULTIPLE = {1, 3, 5, 8, 10} --暗牌倍数
local ZHJ_MING_MULTIPLE = {2, 6, 10, 16, 20} --明牌倍数

local ZJH = {

}

local function make_card_id(sign,number)
    return sign * 100 + number
end

local function shallow_copy(orig)
    local copy
    if type(orig) == "table" then
        copy = {}
        for orig_key, orig_value in pairs(orig) do
            copy[orig_key] = orig_value
        end
    else
        copy = orig
    end
    return copy
end

function ZJH:init(is_control_systerm_win,game_config)
    --生成所有的牌
    local cards = {}
    for i = 1,13 do
        for j = 1,4 do
            table_insert(cards,make_card_id(j,i))
        end
    end

    self.game_status = GAME_STATUS_INIT
    self.table_type = 0
    self.cards = cards
    self.player_cards_map = nil
    self.banker_uid = 0              --庄家uid
    self.next_player_index = 0       --下次出牌人的索引
    self.player_list = {}            --当前游戏中的玩家列表
    self.ming_list = {}              --明牌列表
    self.give_up_list = {}           --放弃列表
    self.fail_list = {}              --失败列表
    self.leave_list = {}             --离开列表
    self.next_bet_score = 1
    self.player_score = {}           --记录玩家的总注金
    self.table_score = {}            --记录桌上的注金单元
    self.run_count = 0               --标记当前进度
    self.run_round = 1               --标记当前的轮数
    self.play_end_time = 0           --下一个出牌玩家的出牌结束时间
    self.compare_list = {}           --比过牌的玩家列表
    self.total_score = 0             --桌面总注额
    self.zhj_ang_score = {}
    self.zhj_ming_score = {}
    self.operate_lock = {}           --操作锁
    --self.game_over_reason = false --是否因为比牌游戏结束
    self.auto_bet_list = {}         --自动下注列表
    self.operate_delay_time = nil   --延长下一个玩家出手的时间
    self.winner_uid    = 0
    self.xianshou_uid  = 0
    self.round_bet_map = {} --玩家每轮下注的金额
    self.max_round_frist_compare_flag = false --满轮首次比牌标记
    self.is_control_systerm_win = is_control_systerm_win
    self.record_mingpai_round = {}     --记录玩家是在第几轮看的牌

    self.record_last_round = {}         --玩家是否切换了轮次
    self.des_win_ming_uid = {}          --已看牌了,被动比牌赢了的玩家uid
    self.last_ming_score = {}           --最后一个看牌加注和跟注的玩家

    self.robot_weight = {}              --返回给客户端测试
    self.robot_use_pic_num = 0          --当局使用表情的机器人数
    self.game_config = game_config      --配置
end

--[[
    判断是否有该玩家了
]]
local function get_player_index(player_list,uid)
    for i,_uid in ipairs(player_list) do
        if _uid == uid then
            return i
        end
    end
end

function ZJH:set_operate_lock(uid,is_lock)
    self.operate_lock[uid] = is_lock
end

function ZJH:get_player_list()
    return self.player_list
end

function ZJH:get_operate_lock(uid)
    if not self.operate_lock[uid] then
        return false
    end
    return self.operate_lock[uid]
end

function ZJH:bind_type(table_type)
    self.table_type = table_type
end

--[[
    将玩家加入到列表中
]]
function ZJH:enter(uid,name)
    if get_player_index(self.player_list,uid) then
        --已经在牌局上了
        return true
    end

    if #self.player_list >= MAX_PLAYER_NUM then
        return false
    end

    table_insert(self.player_list,uid)
    self.compare_list[uid] = {}
    self.auto_bet_list[uid] = false --默认都是不自动跟注
    self.record_last_round[uid] = 1
    self.robot_weight[uid] = 0
    return true
end

function ZJH:check_player_in_round(uid)
     if get_player_index(self.player_list,uid) then
        return true
    end

    return false
end

--[[
    检查游戏是否可以开始
]]
function ZJH:check()
    -- if #self.player_list < MIN_PLAYER_NUM then
    --     return false
    -- end

    assert(self.game_status == GAME_STATUS_INIT)

    self.game_status = GAME_STATUS_STARTED

    local player_cards_map = {}
    for _,_uid in pairs(self.player_list) do
        player_cards_map[_uid] = {}
    end

    self.player_cards_map = player_cards_map

    return true
end

local function shuffle(card_list)
    local n = #card_list
    local shuffled_card_list = {}
    for i = 1, n do
        local index = math_random(1, n - i + 1)
        table_insert(shuffled_card_list, table_remove(card_list, index))
    end
    return shuffled_card_list
end

--[[
    打乱牌
]]
function ZJH:shuffle(swap_times)
    assert(self.game_status == GAME_STATUS_STARTED)
    -- local cards = self.cards

    -- local card_count = #cards
    -- swap_times = card_count or swap_times
    -- for i = 1,swap_times do
    --     local n = math_random(1,card_count)
    --     local m = math_random(1,card_count)
    --     if n ~= m then
    --         cards[n],cards[m] = cards[m],cards[n]
    --     end
    -- end

    self.cards = shuffle(self.cards)
end

--[[
    发牌 每人三张牌
]]
-- function ZJH:deal()
--     assert(self.game_status == GAME_STATUS_STARTED)
--     local cards = self.cards
--     local player_cards_map = self.player_cards_map
--     assert(cards and player_cards_map)

--     local end_index = #self.player_list*zjh_rule.MAX_CARD_COUNT --每人三张牌
--     for i = 1,end_index,#self.player_list do
--         local j = 0
--         for _,card_id_set in pairs(player_cards_map) do
--             table_insert(card_id_set, cards[i + j])
--             j = j + 1
--         end
--     end
--     print("CZJH deal deal deal" .. tostring_r(player_cards_map))
-- end

local function find_best_card_set(player_cards_map,best_uid)
    local uid_has_best_cards
    for uid, player_cards in pairs(player_cards_map) do
        if uid and uid == best_uid then
            goto continue
        end
        if uid_has_best_cards then
            local src_tile_list = player_cards_map[uid]
            local dst_tile_list = player_cards_map[uid_has_best_cards]
            local src_type = zjh_rule.get_card_type(src_tile_list)
            local dst_type = zjh_rule.get_card_type(dst_tile_list)
            if zjh_rule.compare_card(src_type,src_tile_list,dst_type,dst_tile_list) then
                uid_has_best_cards = uid
            end
        else
            uid_has_best_cards = uid
        end
        ::continue::
    end
    return uid_has_best_cards
end

function ZJH:get_best_card_uid()
    local player_cards_map = {}
    for i,uid in ipairs(self.player_list) do
        if self:player_isvalid(uid) then
            player_cards_map[uid] = self.player_cards_map[uid]
        end
    end

    return find_best_card_set(player_cards_map)
end

function ZJH:get_best_card_set(uid)
    local player_cards_map = {}
    print(tostring_r(self.player_list))
    for i,_uid in ipairs(self.player_list) do
        if self:player_isvalid(_uid) and _uid ~= uid then
            player_cards_map[_uid] = self.player_cards_map[_uid]
        end
    end
    print(tostring_r(player_cards_map))
    return find_best_card_set(player_cards_map)
end

function ZJH:get_random_uid(except_uid)
    local uids = {}
    for uid, _ in pairs(self.player_cards_map) do
        if self:player_isvalid(uid) and uid ~= except_uid then
            table_insert(uids, uid)
        end
    end
    return uids[math_random(1, #uids)]
end

local function rand_from_rate(card_type_general_rate)
    local total_number = 0
    for _,rate in pairs(card_type_general_rate) do
        total_number = total_number + rate * 10
    end

    local index = math_random(1,total_number)
    local total = 0
    for card_type,rate in pairs(card_type_general_rate) do
        total = total + rate * 10
        if index <= total then
            return card_type
        end
    end
    return
end

local function del_table_from_list(cards,card_list)
    for i=1,#card_list do
        local card_id = card_list[i]
        for j = 1,#cards do
            if card_id == cards[j] then
                table_remove(cards,j)
                break
            end
        end
    end
end

local function general_gaopaidaiA(cards)
    --先取一张A牌放在第一位
    print("gaopaidaiA")
    for i=1,#cards do
        if cards[i] % 100 == 1 then
            cards[1],cards[i] = cards[i],cards[1]
            break
        end
    end

    for i = 3,#cards do
        local card_type = zjh_rule.get_card_type({cards[1],cards[2],cards[i]})
        if card_type == 1 then
            local card_list = {}
            table_insert(card_list,cards[1])
            table_insert(card_list,cards[2])
            table_insert(card_list,cards[i])
            del_table_from_list(cards,card_list)
            return card_list
        end
    end
end

local function general_gaopaibudaiA(cards)
    --先将所有A牌放牌列表的最后
    for i = 1,#cards do
        if cards[i] % 100 == 1 then
            table_insert(cards,table_remove(cards,i))
        end
    end

    while cards[1]%100 == cards[2]%100 do
        table_insert(cards,table_remove(cards,2))
    end

    for i = 3,#cards do
        local card_type = zjh_rule.get_card_type({cards[1],cards[2],cards[i]})
        if card_type == 1 then
            local card_list = {}
            table_insert(card_list,cards[1])
            table_insert(card_list,cards[2])
            table_insert(card_list,cards[i])
            del_table_from_list(cards,card_list)
            return card_list
        end
    end
end

local function get_card_count_map(cards)
    local card_count_map = {}
    for _,card_id in pairs(cards) do
        local number = card_id % 100
        local t = card_count_map[number]
        if not t then
            t = {}
            card_count_map[number] = t
        end
        table_insert(t,card_id)
    end
    return card_count_map
end

local function general_duizi(cards,min,max)
    local card_count_map = get_card_count_map(cards)
    local duizi_number
    local count = 10

    while count > 0 do 
        local value = math_random(min,max)
        if value == 14 then
            value = 1
        end

        if card_count_map[value] and #card_count_map[value] >= 2 then
            duizi_number = value
            break
        end
        count = count - 1
    end

    --找10次没找到则直接返回
    if not duizi_number then
        return
    end

    for i=1,#cards do
        if cards[i] % 100 ~= duizi_number then
            local card_list = {}
            table_insert(card_list,cards[i])
            for j=1,2 do
                table_insert(card_list,card_count_map[duizi_number][j])
            end
            del_table_from_list(cards,card_list)
            return card_list
        end
    end
end

local function general_shunzi(cards)
    local card_count_map = get_card_count_map(cards)
    for i=1,#cards do
        local value = cards[i] % 100
        local value2 = value + 2
        if value2 == 14 then
            value2 = 1
        end

        if card_count_map[value + 1] and card_count_map[value2] then
            local card_list = {}
            table_insert(card_list,cards[i])
            local get_card = function(value)
                for j=1,#cards do
                    if cards[j] % 100 == value then
                        return cards[j]
                    end
                end
            end
            table_insert(card_list,get_card(value + 1))
            table_insert(card_list,get_card(value2))
            if zjh_rule.get_card_type(card_list) == 3 then
                del_table_from_list(cards,card_list)
                return card_list
            end
        end

        print("ddddd",value,tostring_r(card_count_map))
    end
end

local function get_card_flower_map(cards)
    local flower_map_list = {}
    for i=1,#cards do
        local flower = math_floor(cards[i] / 100)
        flower_list = flower_map_list[flower]
        if not flower_list then
            flower_list = {}
            flower_map_list[flower] = flower_list
        end
        table_insert(flower_list,cards[i])
    end
    return flower_map_list
end

local function general_jinhua(cards)
    local flower_map_list = get_card_flower_map(cards)
    local flower_list = {}
    for _,list in pairs(flower_map_list) do
        if #list >= 3 then
            table_insert(flower_list,list)
        end
    end
    
    if not next(flower_list) then
        return
    end

    --最多找10次
    local count = 10
    while count > 0 do
        local idx = math_random(1,#flower_list)
        local card_list = {}
        local card_flower_list = flower_list[idx]
        for i=1,3 do
            local idx = math_random(1,#card_flower_list)
            table_insert(card_list,table_remove(card_flower_list,idx))
        end
        if zjh_rule.get_card_type(card_list) == 4 then
            del_table_from_list(cards,card_list)
            return card_list
        end
        count = count - 1
    end   
end

local function is_card_exist(card_list,card_id)
    for _,card in pairs(card_list) do
        if card == card_id then
            return true
        end
    end
    return false
end

local function general_shunjin(cards)
    local flower_map_list = get_card_flower_map(cards)
    for i=1,#cards do
        local card_id = cards[i]
        if card_id % 100 <= 12 then
            flower = math_floor(card_id / 100)
            local flower_list = flower_map_list[flower]
            local card_id2 = card_id + 1
            local card_id3 = card_id + 2
            if card_id3 % 100 == 14 then
                card_id3 = flower * 100 + 1
            end

            if is_card_exist(flower_list,card_id2) and is_card_exist(flower_list,card_id3) then
                local card_list = {}
                table_insert(card_list,card_id)
                table_insert(card_list,card_id2)
                table_insert(card_list,card_id3)
                del_table_from_list(cards,card_list)
                return card_list
            end
        end
    end
end

local function general_baozi(cards)
    local card_count_map = get_card_count_map(cards)
    local baozi_list = {}
    for card_value,list in pairs(card_count_map) do
        if #list >= 3 then
            table_insert(baozi_list,list)
        end
    end

    local idx = math_random(1,#baozi_list)
    local list = baozi_list[idx]
    local card_list = {}
    for i=1,3 do
        table_insert(card_list,list[i])
    end

    del_table_from_list(cards,card_list)
    return card_list
end

local function general_random(cards)
    local card_list = {}
    for i = 1, zjh_rule.MAX_CARD_COUNT do
        table_insert(card_list, cards[i])
    end
    del_table_from_list(cards,card_list)
    return card_list
end

function general_cards_by_type(cards,card_type)
    print("===============",card_type)
    if card_type == "gaopaidaiA" then
        return general_gaopaidaiA(cards)
    elseif card_type == "gaopaibudaiA" then
        return general_gaopaibudaiA(cards)
    elseif card_type == "duizi2_9" then
        return general_duizi(cards,2,9)
    elseif card_type == "duizi10_A" then
        return general_duizi(cards,10,14)
    elseif card_type == "shunzi" then
        return general_shunzi(cards)
    elseif card_type == "jinhua" then
        return general_jinhua(cards)
    elseif card_type == "shunjin" then
        return general_shunjin(cards)
    elseif card_type == "baozi" then
        return general_baozi(cards)
    else
        return general_random(cards)
    end
end

local function general_cards(cards)
    local card_type_general = global_configs.card_type_general
    local card_type_general_rate = card_type_general[1]
    local card_type = rand_from_rate(card_type_general_rate)
    return general_cards_by_type(cards,card_type)
end

local function find_bigger_cards(cards,player_cards_map,special_uid)
    local count = 0
    while count < 20 do
        cards = shuffle(cards)
        print_r(cards)
        local t = general_cards(cards)
        if t and #t == 3 then
            local card_type = zjh_rule.get_card_type(t)
            local best_card_type = zjh_rule.get_card_type(player_cards_map[special_uid])
            if zjh_rule.compare_card(card_type,t,best_card_type,player_cards_map[special_uid]) then
                return t
            end
            while #t > 0 do
                table_insert(cards,table_remove(t))
            end
            count = count + 1
        end
    end
end

function ZJH:deal(special_uid,second_uid,deal_card_type)
    dbglog("deal:", tostring(special_uid), tostring(second_uid),deal_card_type)

    assert(self.game_status == GAME_STATUS_STARTED)
    local cards = self.cards
    local player_cards_map = self.player_cards_map
    assert(cards and player_cards_map)

    local index = 1
    for _, player_cards in pairs(player_cards_map) do
        --[[for i = 1, zjh_rule.MAX_CARD_COUNT do
            table_insert(player_cards, cards[index])
            index = index + 1
        end]]
        local t = general_cards(cards)
        if not t or #t ~= 3 then
            print("rrrrrrrrrrrrr",tostring_r(t))
            for i = 1, zjh_rule.MAX_CARD_COUNT do
                table_insert(player_cards, cards[index])
                index = index + 1
            end
            del_table_from_list(cards,player_cards)
        else
            for i = 1,#t do
                table_insert(player_cards,t[i])
            end
        end
    end

    print("fffffffffffffffff",tostring_r(player_cards_map))
    if special_uid and player_cards_map[special_uid] then
        local uid_has_best_cards = find_best_card_set(player_cards_map)
        if uid_has_best_cards ~= special_uid then
            player_cards_map[uid_has_best_cards], player_cards_map[special_uid] =
                player_cards_map[special_uid], player_cards_map[uid_has_best_cards]
        end
    end

    if deal_card_type and second_uid and player_cards_map[second_uid] and 
    special_uid and player_cards_map[special_uid] then
        if deal_card_type == 1 then --second_uid换一副大于special_uid的牌
            local card_list = find_bigger_cards(cards,player_cards_map,special_uid)
            if card_list then
                player_cards_map[second_uid] = card_list     
            else
                local uid_has_second_cards = find_best_card_set(player_cards_map,special_uid)
                player_cards_map[uid_has_second_cards], player_cards_map[second_uid] =
                    player_cards_map[second_uid], player_cards_map[uid_has_second_cards]
                
                --再次交换
                player_cards_map[special_uid], player_cards_map[second_uid] =
                    player_cards_map[second_uid], player_cards_map[special_uid]
            end
        elseif deal_card_type == 2 then --second_uid取第二大的牌
            local uid_has_second_cards = find_best_card_set(player_cards_map,special_uid)
                player_cards_map[uid_has_second_cards], player_cards_map[second_uid] =
                    player_cards_map[second_uid], player_cards_map[uid_has_second_cards]
        end
    end
    print("CZJH deal deal deal" .. tostring_r(player_cards_map))
end

--[[
    获取玩家的牌
]]
function ZJH:get_player_tile_list(uid)
    local player_tile_list = assert(self.player_cards_map[uid],uid)
    return player_tile_list
end

function ZJH:get_all_player_tile_list()
    return self.player_cards_map
end

function ZJH:is_ming_pai(uid)
    if self.ming_list[uid] then
        return true
    end

    return false
end

--获取明牌的人数
function ZJH:get_ming_num()
    local ming_list = self.ming_list
    return #ming_list
end

function ZJH:get_next_compare_player_uid()
    local next_player_index = self.next_player_index
    local tmp_next_player_index
    for index = 1, MAX_PLAYER_NUM, 1 do
        tmp_next_player_index = (next_player_index + index) % MAX_PLAYER_NUM
        if tmp_next_player_index == 0 then
            tmp_next_player_index = MAX_PLAYER_NUM
        end

        if self.player_list[tmp_next_player_index] then
            if self:player_isvalid(self.player_list[tmp_next_player_index]) and self.next_player_index ~= tmp_next_player_index then
                return self.player_list[tmp_next_player_index]
            end
        end
    end
end

function ZJH:get_player_bet_score(uid)
    if not self.player_score[uid] then
        return 0
    end
    return self.player_score[uid]
end

function ZJH:get_curr_round_score(uid)
    if not self.round_bet_map[uid] then
        return 0
    end

    if (self.round_bet_map[uid].score or 0) > 0 then
        return self.round_bet_map[uid].score or 0
    end

    return self.round_bet_map[uid].dizhu or 0
end

function ZJH:clear_curr_round_score(uid)
    self.round_bet_map[uid] = nil
end

function ZJH:is_fail(uid)
    if not self.fail_list[uid] then
        return false
    end

    return true
end

function ZJH:is_give_up(uid)
    if self.give_up_list[uid] then
        return true
    end

    return false
end

--[[
    获取玩家牌信息
]]
function ZJH:get_player_tile_info()
    local player_tite_info_list = {}
    for _,tmp_uid in pairs(self.player_list) do
        --没有离开游戏的玩家才需要填充数据
        if not self:is_leave(tmp_uid) then
            local player_tile_info = {}
            --明牌过的玩家才需要发送牌
            if self:is_ming_pai(tmp_uid) then
                player_tile_info.uid = tmp_uid
                player_tile_info.tile_list = self:get_player_tile_list(tmp_uid)
            else
                player_tile_info.uid = tmp_uid
                player_tile_info.tile_list = {}
            end
            --方便测试把所有牌都亮出来
            player_tile_info.uid = tmp_uid
            --player_tile_info.tile_list = self:get_player_tile_list(tmp_uid)
            player_tile_info.cur_bet_score = self:get_player_bet_score(tmp_uid)
            player_tile_info.is_fail = self:is_fail(tmp_uid)
            player_tile_info.is_give_up = self:is_give_up(tmp_uid)
            player_tile_info.cur_round_score = self:get_curr_round_score(tmp_uid)
            table_insert(player_tite_info_list,player_tile_info)
        end
    end

    return player_tite_info_list
end

function ZJH:get_banker_uid()
    return self.banker_uid
end

function ZJH:is_xianshou(uid)
    if self.xianshou_uid == uid then
        return true
    end
    return false
end

function ZJH:get_table_total_score()
    return self.total_score
end

--[[
    设置庄家
]]
function ZJH:setingbanker_status(_banker_uid)
    assert(self.game_status == GAME_STATUS_STARTED)
    local player_list = self.player_list
    if not _banker_uid then
        self.banker_uid = player_list[math_random(1,#player_list)]
    else
        --查找一下是否存在庄家了
        local have_banker = false
        for _,uid in pairs(player_list) do
            if uid == _banker_uid then
                self.banker_uid = _banker_uid
                have_banker = true
                break
            end
        end

        if not have_banker then
            self.banker_uid = player_list[math_random(1,#player_list)]
        end
    end

    --轮到庄家的下一位出手
    self:trun_banker_next_player()
end

--[[
    是否轮到自己的
]]
function ZJH:is_my_turn(uid)
    return self.next_player_index == get_player_index(self.player_list,uid)
end

function ZJH:set_operate_delay_time(delay_time)
    if delay_time then
        self.operate_delay_time = util.get_now_time() + delay_time
    else
        self.operate_delay_time = util.get_now_time() + COMPARE_ANIMATION_TIME
    end
end

function ZJH:get_operate_delay_time()
    return self.operate_delay_time
end

function ZJH:clear_operate_delay_time()
    if self.operate_delay_time then
        self.operate_delay_time = nil
    end
end

function ZJH:get_round_count()
    return self.run_count
end


function ZJH:set_max_round_frist_compare_delay_time(delay_time)
    if self.max_round_frist_compare_flag then
        return
    end
    self.max_round_frist_compare_flag = true

    self:set_operate_delay_time(delay_time)
end

--[[
    庄家下一位
]]
function ZJH:trun_banker_next_player()
    local next_player_index = get_player_index(self.player_list,self.banker_uid) + 1
    if next_player_index > #self.player_list then
        next_player_index = next_player_index % #self.player_list
    end
    self.next_player_index = next_player_index
    self.xianshou_uid =self.player_list[next_player_index] 
    self.play_end_time = util.get_now_time() + PLAY_TIME
end

--[[
    增加游戏进度
]]
function ZJH:add_run_count()
    self.run_count = self.run_count + 1
    if self.run_count == MAX_PLAYER_NUM then
        self.run_round = self.run_round + 1
--        print("new round new round" .. self.run_round)
        self.run_count = 0
    end
end

function ZJH:get_run_round()
    return self.run_round
end

--[[
    轮到下一位操作
]]
function ZJH:turn_next_player()
    local next_player_index = self.next_player_index
    local tmp_next_player_index
    for index = 1, MAX_PLAYER_NUM, 1 do
        tmp_next_player_index = (next_player_index + index) % MAX_PLAYER_NUM
        if tmp_next_player_index == 0 then
            tmp_next_player_index = MAX_PLAYER_NUM
        end
        if self:is_started() then
            self:add_run_count()
        end

        if self.player_list[tmp_next_player_index] then
            if self:player_isvalid(self.player_list[tmp_next_player_index]) then
                self.next_player_index = tmp_next_player_index
                --判断切换的时候需要加上比牌时间不
                if self:get_operate_delay_time() then
                    self.play_end_time = util.get_now_time() + PLAY_TIME + COMPARE_ANIMATION_TIME
                else
                    self.play_end_time = util.get_now_time() + PLAY_TIME
                end
                break
            end
        end
    end
end

function ZJH:reset_play_end_time()
    if self:get_operate_delay_time() then
        self.play_end_time = util.get_now_time() + PLAY_TIME + COMPARE_ANIMATION_TIME
    else
        self.play_end_time = util.get_now_time() + PLAY_TIME
    end
end

function ZJH:get_compare_player_uid()
    local compare_list = {}
    local next_player_uid = self:get_next_player_uid()
    for _,uid in pairs(self.player_list) do
        if self:player_isvalid(uid) and uid ~= next_player_uid then
            table_insert(compare_list,uid)
        end
    end

    if #compare_list > 1 then
        local index = math_random(1,#compare_list)
        return compare_list[index]
    end

    return compare_list[1]
end

--[[
    获取下一个操作的uid
]]
function ZJH:get_next_player_uid()
    return self.player_list[self.next_player_index] or 0
end

--[[
    判断是否已经看过牌了
]]
function ZJH:have_look_card(uid)
    return self.ming_list[uid]
end

function ZJH:can_look(uid)
    if self:is_game_over() then
        errlog(uid, 'CZJH:look_card game is over')
        return false
    end

    if not get_player_index(self.player_list,uid) then
        errlog(uid, 'CZJH:can_look no in game')
        return false
    end

    if not self:player_isvalid(uid) then
        errlog(uid, 'CZJH:can_look player is not valid')
        return false
    end

    local roomdata = self.game_config
    if not roomdata then
        errlog(self.table_type, "CZJH can_look not find roomdata")
        return false
    end

    --如果已经明牌过了
    if self:is_ming_pai(uid) then
        return false
    end

    local max_look_round = (roomdata.max_look_round and roomdata.max_look_round or 1)
    if self.run_round >= max_look_round then
        return true
    end

    return false
end

--[[
    查看牌操作
]]
function ZJH:look_card(uid)
    self.ming_list[uid] = true
    local src_tile_list = self:get_player_tile_list(uid)
    local card_type = zjh_rule.get_card_type(src_tile_list)
    self.record_mingpai_round[uid] = self.run_round
    return card_type, src_tile_list
end

--[[
    判断某个玩家是否还有效
]]
function ZJH:player_isvalid(uid)
    if self.give_up_list[uid] or self.fail_list[uid] or self.leave_list[uid] then
        return false
    end

    return true
end

--获取明牌且未弃牌的人数
function ZJH:get_valid_ming_player(except_uid)
    local list = {}
    for uid, _ in pairs(self.ming_list) do
        if self:player_isvalid(uid) and uid ~= except_uid then
            table_insert(list,uid)
        end
    end
    return list
end

function ZJH:is_leave(uid)
    if not self.leave_list[uid] then
        return false
    end
    return true
end

--[[
    判断场上还剩多少人
]]
function ZJH:get_left_num()
    local count = 0
    for _,_uid in pairs(self.player_list) do
        if self:player_isvalid(_uid) then
            count = count + 1
        end
    end

    return count
end

function ZJH:can_give_up(uid)
    --检查游戏是否已经结束了
    if self:is_game_over() then
        errlog(uid, 'CZJH:give_up game is over')
        return false
    end

    if not get_player_index(self.player_list,uid) then
        errlog(uid, 'CZJH:look_card no in game')
        return false
    end

    --判断该玩家是否还有效
    if not self:player_isvalid(uid) then
        errlog(uid, 'CZJH:give_up player is not valid')
        return false
    end

    return true
end

function ZJH:is_winner(uid)
    return uid == self.winner_uid
end

--[[
    放弃操作
]]
function ZJH:give_up(uid)
    --标记一下放弃
    self.give_up_list[uid] = true

    if not self:is_game_over() then
        if self:is_my_turn(uid) then
            return true
        end
    end
    return false
end

--[[
    通知当前局有人离开了
]]
function ZJH:leave(uid)
    if not get_player_index(self.player_list,uid) then
        dbglog(uid, 'CZJH:look_card no in game')
        return false
    end

    --判断该玩家是否还有效
    if self:is_leave(uid) then
        dbglog(uid, 'CZJH:leave player is leave')
        return false
    end

    --标记一下离开
    self.leave_list[uid] = true

    if not self:is_game_over() then
        if self:is_my_turn(uid) then
            return true
        end
    end

    return false

end

function ZJH:set_winner()
    for _,_uid in pairs(self.player_list) do
        if self:player_isvalid(_uid) then
            self.winner_uid = _uid
            break
        end
    end
end

function ZJH:is_game_over()
    if self.game_status == GAME_STATUS_OVER then
        return true
    end
    local count = self:get_left_num()
    if count >= 2 then
        return false
    end
    self:set_winner()
    self.game_status = GAME_STATUS_OVER
    self:empty_weight()
    return true
end


function ZJH:is_started()
    return self.game_status == GAME_STATUS_STARTED
end

function ZJH:add_player_score(uid,score,is_dizhu)
    if not self.player_score[uid] then
        self.player_score[uid] = score
    else
        self.player_score[uid] = self.player_score[uid] + score
    end

    if not self.round_bet_map[uid] then
        self.round_bet_map[uid] = {round = self.run_round,score = 0,dizhu = score}
    end

    if not is_dizhu then --不把底注算进去
        if self.round_bet_map[uid].round ~= self.run_round then
            self.round_bet_map[uid].score = 0
        end
        self.round_bet_map[uid].score = self.round_bet_map[uid].score + score
    end
end

function ZJH:add_table_score(score)
    table_insert(self.table_score,score)
    self.total_score = self.total_score + score
end

function ZJH:get_room_dizhu()
    local roomdata = self.game_config
    if not roomdata then
        errlog(self.table_type, "CZJH bet_begin_score not find roomdata")
        return
    end
    return roomdata.dizhu
end

function ZJH:get_room_max_round()
    local roomdata = self.game_config
    if not roomdata then
        errlog(self.table_type, "CZJH bet_begin_score not find roomdata")
        return
    end
    return roomdata.comparable_bet_round
end

--[[
    开局下的底注
]]
function ZJH:bet_begin_score()
    local roomdata = self.game_config
    if not roomdata then
        errlog(self.table_type, "CZJH bet_begin_score not find roomdata")
        return
    end

    self.next_bet_score = roomdata.dizhu

    for _, uid in pairs(self.player_list) do
        self:add_player_score(uid,self.next_bet_score,true)
        self:add_table_score(self.next_bet_score)
    end
end

function ZJH:get_can_bet_score(uid)
    if self:is_ming_pai(uid) then
        return self.next_bet_score * 2
    end
    return self.next_bet_score
end

function ZJH:get_ding_zhu(uid)
    local roomdata = self.game_config
    if not roomdata then
        errlog(self.table_type, "not find roomdata")
        return 0
    end

    if self:is_ming_pai(uid) then
        return roomdata.dingzhu * 2
    end
    return roomdata.dingzhu
end

function ZJH:get_add_bet_info(uid)
    local dingzhu = self:get_ding_zhu(uid)
    local scale = 1
    for _, bet_range in pairs(constant.ZJH_ADD_BET_RANGE) do
        if dingzhu >= bet_range[1] and dingzhu <= bet_range[2] then
            scale = bet_range[3]
            break
        end
    end
    
    local roomdata = self.game_config
    if roomdata then
        scale = roomdata.dizhu
    end

    local bet_score = self.next_bet_score
    if self:is_ming_pai(uid) then
        bet_score = bet_score*2 + scale
    else
        bet_score = bet_score + scale
    end

    return bet_score,dingzhu,scale
end

function ZJH:score_isvalid(uid,score)
    --检测下注范围是否落在有效范围内
    local bet_score = self:get_can_bet_score(uid)
    local min_add_bet_score,max_add_bet_score,scale = self:get_add_bet_info(uid)
    if score < bet_score or score > max_add_bet_score then
        errlog(uid, 'ZJH:score_isvalid score <= bet_score or score > max_add_bet_score',score,bet_score,max_add_bet_score)
        return false
    end

    -- if score == bet_score or (score - min_add_bet_score)%scale == 0 then
    --     return true
    -- end

    --errlog(uid, 'score ~= bet_score or (score - min_add_bet_score)/scale ~= 0',score,bet_score,min_add_bet_score)
    return true
end

function ZJH:can_add_score(uid,score)
    local roomdata = self.game_config
    if not roomdata then
        errlog(uid, 'CZJH:add_score roomdata is nil')
        return false
    end

    if self:is_game_over() then
        errlog(uid, 'CZJH:add_score game is over')
        return false
    end

    if not get_player_index(self.player_list,uid) then
        errlog(uid, 'CZJH:add_score no in game')
        return false
    end

    --检测是不是轮到我出手
    if not self:is_my_turn(uid) then
        errlog(uid, 'CZJH:add_score no is my trun')
        return false
    end

    if not self:player_isvalid(uid) then
        errlog(uid, 'CZJH:add_score failed because player is no valid')
        return false
    end

    --当前进行的轮数大于最大可比轮数
    if score then
        if self.run_round >= roomdata.comparable_bet_round then
            errlog(uid, 'CZJH:add_score self.run_round >= roomdata.comparable_bet_round')
            return false
        end

        --判断当前玩家下注的金额数是否合法
        if not self:score_isvalid(uid,score) then
            errlog(uid, 'CZJH:add_score not self:score_isvalid(uid,score)')
            return false
        end

    else
        score = self:get_can_bet_score(uid)
        if self.run_round >= roomdata.comparable_bet_round then
            score = 0
        end
    end

    return true,score
end
--[[
    请求加注
]]
function ZJH:add_score(uid,score,enough_money,add_type)
    if enough_money then
        --保存当前家的下注积分
        if self:is_ming_pai(uid) then
            self.next_bet_score = math_ceil(score / 2)
        else
            self.next_bet_score = score
        end
    end

    self:add_player_score(uid,score)
    self:add_table_score(score)
    self:record_last_ming_score(uid,add_type)

    return true
end

--[[
    加入到失败列表中
]]
function ZJH:add_fail_list(uid)
    self.fail_list[uid] = true
end

--[[
    加入到比牌列表中
]]
function ZJH:add_compare_list(uid,dst_uid)
    table_insert(self.compare_list[uid],dst_uid)
end

function ZJH:is_can_compare(uid)
    local roomdata = self.game_config
    if not roomdata then
        errlog(self.table_type, "CZJH is_can_compare not find roomdata")
        return false
    end

    if self.run_round < roomdata.max_bet_round then
        return false
    end

    return true
end

function ZJH:can_compare(uid,dst_uid,is_force)
    if self:is_game_over() then
        errlog(uid, 'CZJH:compare_card game is over')
        return false
    end

    --检测是不是轮到我出手
    if not self:is_my_turn(uid) then
        errlog(uid, 'CZJH:compare_card no is my trun')
        return false
    end

    if not get_player_index(self.player_list,uid) then
        errlog(uid, 'CZJH:compare_card no in game')
        return false
    end

    if not get_player_index(self.player_list,dst_uid) then
        errlog(dst_uid, 'CZJH:compare_card no in game')
        return false
    end

    if not self:player_isvalid(uid) then
        errlog(uid, 'ZJH:compare_card player is not valid')
        return false
    end

    if not self:player_isvalid(dst_uid) then
        errlog(dst_uid, 'ZJH:compare_card player is not valid')
        return false
    end

    if is_force then
        return true
    end

    local roomdata = self.game_config
    if not roomdata then
        errlog(self.table_type, "CZJH can_compare not find roomdata")
        return false
    end

    if self.run_round < roomdata.max_bet_round then
        return false
    end

    return true
end

--[[
    比牌
]]
function ZJH:compare(uid,dst_uid)
    --加入到比牌列表中
    self:add_compare_list(uid,dst_uid)
    self:add_compare_list(dst_uid,uid)

    local src_tile_list = self:get_player_tile_list(uid)
    local dst_tile_list = self:get_player_tile_list(dst_uid)
    local src_type = zjh_rule.get_card_type(src_tile_list)
    local dst_type = zjh_rule.get_card_type(dst_tile_list)
    local win = zjh_rule.compare_card(src_type,src_tile_list,dst_type,dst_tile_list)

    --加入到失败列表中
    if win then
        self:add_fail_list(dst_uid)
    else
        self:add_fail_list(uid)
        self:add_ming_win_list(dst_uid)
    end

    local is_trun_next = false
    if not self:is_game_over() then
        if self:is_my_turn(uid) then
            is_trun_next = true
        end
    end

    return win,is_trun_next,src_type,dst_type
end

-- function ZJH:is_game_over_by_compare()
--     return self.game_over_reason == GAME_OVER_BY_COMPARE
-- end

function ZJH:get_play_end_time()
    return self.play_end_time
end

function ZJH:get_game_result()
    if not self:is_game_over() then
        return false
    end

    if not self.player_score then
        return false
    end

    local winners = {}
    local losers = {}
    for uid, score in pairs(self.player_score) do
        local player_record = {}
        player_record.uid = uid

        if not self:player_isvalid(uid) and not self:is_winner(uid) then
            player_record.add_score = -score
        else
            player_record.add_score = self:get_table_total_score() - score
        end

        if player_record.add_score > 0 then
            local tile_list = self:get_player_tile_list(uid)
            player_record.card_type = zjh_rule.get_card_type(tile_list)
            table_insert(winners, player_record)
        else
            table_insert(losers, player_record)
        end

    end

    return true, {winners = winners,losers = losers}
end

function ZJH:get_card_type(uid)
    local tile_list = self:get_player_tile_list(uid)
    return zjh_rule.get_card_type(tile_list)
end

function ZJH:get_compare_card_list(uid)
    local player_card_list = {}
    for _, dst_uid in pairs(self.compare_list[uid]) do
        --跟我比牌的人没有离开游戏那么久可以看到对方的牌
        if not self:is_leave(dst_uid) then
            local player_card = {}
            player_card.uid = dst_uid
            player_card.tile_list = self:get_player_tile_list(dst_uid)
            player_card.card_type = zjh_rule.get_card_type(player_card.tile_list)
            table_insert(player_card_list, player_card)
        end
    end

    local player_card = {}
    player_card.uid = uid
    player_card.tile_list = self:get_player_tile_list(uid)
    player_card.card_type = zjh_rule.get_card_type(player_card.tile_list)
    table_insert(player_card_list, player_card)

    return player_card_list
end

function ZJH:get_buttons_status(uid,coins,is_robot)
    local player_buttons_status = {}
    local roomdata = self.game_config
    if roomdata then
        if get_player_index(self.player_list,uid) and self:player_isvalid(uid) then
            player_buttons_status.can_look = self:can_look(uid)
            if not self:is_my_turn(uid) then
                player_buttons_status.can_compare = false
                player_buttons_status.can_bet = false
                player_buttons_status.can_continue_bet = true
                player_buttons_status.is_auto_bet = self:is_auto_bet(uid)
            else
                --如果剩余的金币小于身上金币值那么只能比牌了
                local bet_score = self:get_can_bet_score(uid)
                --取消强制比牌,设置成可以比牌 可弃牌
                if (coins or 0) < bet_score then
                    player_buttons_status.can_compare = true
                    player_buttons_status.can_bet = false
                    player_buttons_status.can_continue_bet = false
                    player_buttons_status.is_auto_bet = false
                    --if is_robot then
                    --    player_buttons_status.force_compare_uid = self:get_next_compare_player_uid()
                    --end
                else
                    if self.run_round >= roomdata.comparable_bet_round then
                        player_buttons_status.can_compare = false
                        player_buttons_status.can_bet = false
                        player_buttons_status.can_continue_bet = false
                        player_buttons_status.is_auto_bet = false
                        player_buttons_status.force_compare_uid = self:get_next_compare_player_uid()
                    else
                        player_buttons_status.can_compare = self:is_can_compare(uid)
                        local min_add_bet_score,max_add_bet_score,scale = self:get_add_bet_info(uid)
                        player_buttons_status.min_add_bet_score = min_add_bet_score
                        player_buttons_status.max_add_bet_score = max_add_bet_score
                        player_buttons_status.scale = scale
                        if player_buttons_status.min_add_bet_score > player_buttons_status.max_add_bet_score then
                            player_buttons_status.can_bet = false
                            player_buttons_status.min_add_bet_score = player_buttons_status.max_add_bet_score
                        else
                            player_buttons_status.can_bet = true
                        end
                        player_buttons_status.can_continue_bet = true
                        --自动跟注状态下不能比牌和不能加注了
                        player_buttons_status.is_auto_bet = self:is_auto_bet(uid)
                        if player_buttons_status.is_auto_bet then
                            player_buttons_status.can_bet = false
                            player_buttons_status.can_compare = false
                        end
                        player_buttons_status.bet_score = bet_score
                    end
                end
                --player_buttons_status.full_bet_list = self:get_full_score_list(uid)
            end
        end
    end
    return player_buttons_status
end

--[[
    获取游戏当前状态信息
]]
function ZJH:get_game_status(uid,coins,is_robot)
    local game_status = {}
    game_status.banker_uid = self:get_banker_uid()
    game_status.cur_status = self.game_status
    game_status.player_tile_info_list = self:get_player_tile_info()
    game_status.next_player_uid = self:get_next_player_uid()
    game_status.bet_list = self.table_score
    game_status.run_cycle = self.run_round
    local curr_time = util.get_now_time() 
    local play_end_time = self:get_play_end_time()
    if play_end_time > curr_time then
        game_status.next_player_operate_time = play_end_time - curr_time
    end
    game_status.total_score = self:get_table_total_score()
    game_status.buttons_status = self:get_buttons_status(uid,coins,is_robot)
    return game_status
end

function ZJH:get_game_type()
    return 1
end

function ZJH:set_auto_bet(uid,is_auto)
    if self:is_game_over() then
        errlog(uid, 'CZJH:set_auto_bet game is over')
        return false
    end

    if not get_player_index(self.player_list,uid) then
        errlog(uid, 'CZJH:set_auto_bet no in game')
        return false
    end

    if not self:player_isvalid(uid) then
        errlog(uid, 'CZJH:set_auto_bet player is not valid')
        return false
    end

    if is_auto then
        self.auto_bet_list[uid] = true
    else
        self.auto_bet_list[uid] = false
    end
end

function ZJH:is_auto_bet(uid)
    return self.auto_bet_list[uid]
end

function ZJH:get_valid_player_num()
    local num = 0
    for _, uid in pairs(self.player_list) do
        if self:player_isvalid(uid) then
            num = num + 1
        end
    end

    return num
end

function ZJH:is_control_system_win()
    return self.is_control_systerm_win
end

function ZJH:get_mingpai_round(uid)
    return self.record_mingpai_round[uid]
end

function ZJH:get_last_round(uid)
    return self.record_last_round[uid]
end

function ZJH:set_last_round(uid)
    self.record_last_round[uid] = self.run_round
end

function ZJH:empty_weight()
    for _,_uid in pairs(self.player_list) do
        self.robot_weight[_uid] = 0
    end
end

function ZJH:set_robot_weight(uid,weight)
    self.robot_weight[uid] = weight
end

function ZJH:get_robot_weight(uid)
    return self.robot_weight[uid]
end

function ZJH:add_ming_win_list(uid)
    for k, _uid in pairs(self.des_win_ming_uid) do
        if _uid == uid then
            return
        end
    end
    if self:is_ming_pai(uid) then
        table_insert(self.des_win_ming_uid,uid)
    end
end

function ZJH:get_ming_win_list()
    return self.des_win_ming_uid
end

function ZJH:update_robot_compare_list(except_uid)
    for k=#self.des_win_ming_uid,1,-1 do
        local uid = self.des_win_ming_uid[k]
        if not self:player_isvalid(uid) or uid == except_uid then
            table_remove(self.des_win_ming_uid,k)
        end 
    end

    for k, uid in pairs(self.last_ming_score) do
        if not self:player_isvalid(uid) or uid == except_uid then
            self.last_ming_score[k] = nil
        end 
    end
end

--1、明牌加注；2、明牌跟注；3、焖牌加注
function ZJH:record_last_ming_score(uid,add_type)
    if self:is_ming_pai(uid) then
        self.last_ming_score[add_type] = uid
    elseif not self:is_ming_pai(uid) and add_type == 1 then
        self.last_ming_score[3] = uid          
    end
end

function ZJH:get_ming_score_record()
    return self.last_ming_score
end

function ZJH:robot_can_use_pic(pic_conf)
    if not pic_conf or not pic_conf.select_pic then 
        return 
    end
    if self.robot_use_pic_num >= 2 then --单局最多允许2个机器人使用表情
        return
    end
    local use_rate = pic_conf.use_rate or 0
    local use_pic = pic_conf.select_pic 
    if math_random(1,100) <= pic_conf.use_rate then
        local select_pic = use_pic[math_random(1,#use_pic)]
        local delay_time
        if pic_conf.delay_time then
            delay_time = math_random(pic_conf.delay_time[1],pic_conf.delay_time[2])
        end
        return select_pic,delay_time
    end

    return false
end

function ZJH:add_use_pic_num()
    self.robot_use_pic_num = self.robot_use_pic_num + 1
end

M.MAX_PLAYER_NUM         = MAX_PLAYER_NUM
M.MIN_PLAYER_NUM         = MIN_PLAYER_NUM
M.MAX_WATCH_PLAYER_NUM   = MAX_WATCH_PLAYER_NUM
M.GAME_REST_TIME         = GAME_REST_TIME
M.COINS_ANIMATION_TIME   = COINS_ANIMATION_TIME
M.COMPARE_ANIMATION_TIME = COMPARE_ANIMATION_TIME
M.PLAY_TIME              = PLAY_TIME

function M.new()
    local o = {}
    return setmetatable(o,{__index = ZJH})
end

return M
